/******************************************************************************
 cnome: A C++ source "genes" discover. :-)

 See more details at:

   http://www.codeplex.com/cnome

 The original author site is at: http://www.alemosbarbosa.com/

 Copyright (c) 2008-2010 Alexandre Lemos Barbosa.
 Distributed under the MIT License. (See accompanying file LICENSE-MIT.txt 
 or at http://www.codeplex.com/cnome/license)
 ******************************************************************************/

#include "cnome_config.h"

#include <exception>
#include <utility>
#include <iostream>

#include <boost/wave/cpplexer/cpp_lex_token.hpp>

#include "symbol.h"

using namespace boost::wave;
using namespace std;

namespace cnome {
cnome_symbolsT cnome_symbols;

const char* symbol::strSymbolType[last_symbolType] = {
        "unknown",
        "internal",
        "keyword",  // char, int, long, float, double, boolean
        "member",
        "method",
        "enumerator",
        "enun_name",
        "parameter",
		"template_parameter",
        "typedefname",
        "pointer to function",
        "template_variable",
        "other" };

const char* symbol::strStatusDD[last_statusDD] = {
        "unknow",
        "declared",
        "defined" };

symbol::symbol(): m_sblTyp(unknown),  m_typeTk(symbolPtr(static_cast<symbol*>(0))), m_scope(symbolPtr(static_cast<symbol*>(0))),
        m_stt(declared), m_str(0), m_inners(), m_inrsCreated(),
        m_paramPos(-1), m_identifierType(NONE), m_pos(0) {
    update_str();
}
    
symbol::symbol(token_id argTkId, std::size_t a_pos):m_sblTyp(unknown), m_typeTk(symbolPtr(static_cast<symbol*>(0))),
        m_scope(symbolPtr(static_cast<symbol*>(0))), m_stt(declared), m_str(0), m_inners(), m_inrsCreated(),
        m_paramPos(-1), m_identifierType(NONE), m_pos(a_pos) {
    set_token_id(argTkId);
    update_str();
}

symbol::symbol(const token_t& argTk, symbolType s, std::size_t a_pos): token_t(argTk), m_sblTyp(s), m_typeTk(symbolPtr(static_cast<symbol*>(0))),
        m_scope(symbolPtr(static_cast<symbol*>(0))), m_stt(declared), m_str(0), m_inners(), m_inrsCreated(),
        m_paramPos(-1), m_identifierType(NONE), m_pos(a_pos) {
    //m_definitions.push_back(argTk);
    update_str();
}

symbol::symbol(const symbol& s):  token_t(s), m_definitions(s.m_definitions), m_declarations(s.m_declarations),
        m_references(s.m_references), m_sblTyp(s.m_sblTyp), m_typeTk(s.m_typeTk),
        m_scope(s.m_scope), m_stt(s.m_stt), m_str(0), m_adorners(s.m_adorners), 
        m_inners(s.m_inners), m_inrsCreated(s.m_inrsCreated), m_paramPos(s.m_paramPos), m_identifierType(s.m_identifierType), 
        m_pos(s.m_pos) {
    if (s.m_scope.get() == this)
       throw exception();
       //throw exception("Internal error");

    update_str();
}

symbol::~symbol() {
    delete[] m_str;
}

void  symbol::set_type_symbol(const symbolPtr s) {m_typeTk=s; }

void  symbol::set_scope(const symbolPtr s) {
    if (s.get() == this)
      // throw exception("Internal error");
       throw exception();
    m_scope = s;
    update_str();
}

void symbol::update_str() {
    const unsigned int MAXSCOPE = 10000;
    std::string scp;

    if (*this != T_IDENTIFIER && *this != T_OPERATOR && (*this != T_ELLIPSIS || (*this == T_ELLIPSIS && !m_scope))) {
        scp = get_value().c_str();
    }
    else {
        std::list<const symbol*> lstSmbItem;
        const symbol* it;
        unsigned int count = 0;
        for (it=this; it && count < MAXSCOPE; it = it->m_scope.get(), count++)
            lstSmbItem.push_back(it);
        if (count >= MAXSCOPE)
            //throw exception("Internal error");
            throw exception();

        boost::wave::token_id prevTk=T_FIRST_TOKEN;
        std::string aux = "::";
        for (bool goOut=false; !goOut&&lstSmbItem.size()&&
            (*lstSmbItem.back()).get_value().size(); 
            lstSmbItem.pop_back()) {
            it=lstSmbItem.back();
            if (it->get_symbol_type() == symbol::point_to_function) {
                aux = "";
            }
            //else if (it->get_symbol_type() == symbol::template_parameter) {
            //	aux = "::";
            //}
            scp += aux + it->get_value().c_str();
            token_t tkaux = *it;
            switch(*it) {
                case T_CHAR:
                case T_WCHART:
                case T_BOOL:
                case T_SHORT:
                case T_INT:
                case T_LONG:
                case T_SIGNED:
                case T_UNSIGNED:
                case T_FLOAT:
                case T_DOUBLE:
                case T_VOID:
                    scp = it->get_value().c_str();
                    goOut=true;
                    break;
                case T_IDENTIFIER:
                    if (it->get_symbol_type() == symbol::internal) {
                        scp = it->get_value().c_str();
                        goOut = true;
                    } 
                    else {
                        switch(it->get_identifier_type()) {
                            case T_CLASS_NAME:
                            case T_ENUM_NAME:
                            case T_TEMPLATE_NAME:
                                 aux = "::";
                                 break;
                            case T_ORIGINAL_NAMESPACE_NAME:
                                if (    static_cast<symbol::identifierType>(prevTk) == T_CLASS_NAME || static_cast<symbol::identifierType>(prevTk) == T_ENUM_NAME
                                     || static_cast<symbol::identifierType>(prevTk) == T_TEMPLATE_NAME)
                                     scp = std::string("::") + it->get_value().c_str();
                                aux = "::";
                                break;
                            default:
                                aux = "$";
                                break;
                        }
                    }
                    break;
                default:
                    aux = "$";
                    break;
            }
            prevTk = *it;
        }
    }
    delete[] m_str; // Free previous value
    m_str = new char[scp.size()+1];
    strcpy(m_str, scp.c_str());
}

symbol& symbol::operator=(const symbol& s) {
    m_definitions = s.m_definitions;
    m_declarations = s.m_declarations;
    m_references = s.m_references;
    m_typeTk=s.m_typeTk;
    m_sblTyp=s.m_sblTyp;
    if (s.m_scope.get() == this)
        //throw exception("Internal error");
        throw exception();
    m_scope=s.m_scope;
    m_stt=s.m_stt;
    delete[] m_str; // Free previous value
    m_str = new char[(s.m_str?strlen(s.m_str):0)+1];
    strcpy(m_str, s.m_str?s.m_str:"");
    m_adorners = s.m_adorners;
    m_inners = s.m_inners;
    m_inrsCreated = s.m_inrsCreated;
    m_paramPos = s.m_paramPos;
    m_identifierType = s.m_identifierType;

    return *this;
}

symbolPtr symbol::add(symbolPtr pinr) {
    m_inners.push_back(pinr);
    
    return pinr;
}

const symbolPtr symbol::add(const symbol& inr) {
    symbolPtr l_pinr = symbolPtr(new symbol(inr));
    m_inrsCreated.push_back(l_pinr);  // TODO: study the functionality of m_inrsCreated. ALB 27apr06
    
    return add(l_pinr);
}

std::string adns2Str(const adornersT& adns) {
    std::string ret;
	for (adornersT::const_iterator it = adns.begin(); it!=adns.end(); it++) {
        ret += *it==T_CONST?string(" ")+it->get_value().c_str():it->get_value().c_str();
	}

    return ret;
}

class ReferenceNotNull_Pred : public unary_function<std::pair<iterator, symbolPtr>, bool>{
public:
	bool operator()(std::pair<iterator, symbolPtr> const& c) const { return c.second; }
};

std::ostream& symbol::summary(std::ostream& os) const {
	os << to_str() << ": ";
	if (*this == T_IDENTIFIER) {
		symbol::identifierType idTyp = get_identifier_type();
		if (idTyp > symbol::NONE && idTyp < symbol::last_identifierType) {
			os << symbol::strSymbolType[idTyp];
		}
		else if (idTyp == symbol::NONE){
			if (get_type_symbol()) {
				os << (   get_type_symbol()->to_str()
                        + adns2Str(get_type_symbol()->get_adorners())
                        + adns2Str(get_adorners())
                       ).c_str();
			}
			os << ": IDENTIFIER"; 
		}
	}
	else if (get_symbol_type()==symbol::keyword) {
		os << "KEYWORD";
	}
	else if (*this == T_ELLIPSIS) {
		os << "VAR_PARAMETER";
	}
	else {
		os << "UNKNOWN";
	}
    os << ": ";
	if  (*this == T_IDENTIFIER && get_identifier_type() == symbol::NONE) {
		os << symbol::strSymbolType[get_symbol_type()];
	}
    os << ": ";
	if (*this == T_IDENTIFIER && get_identifier_type() == symbol::T_TYPEDEF_NAME && get_type_symbol()) {
		os << (   get_type_symbol()->to_str() 
                + adns2Str(get_type_symbol()->get_adorners())
                + adns2Str(get_adorners())
              ).c_str();
	}
	if (get_symbol_type() != symbol::keyword) {
		os << ": (" << get_position().get_file().c_str() << ":" << get_position().get_line() << "," 
		            << get_position().get_column() << ")";
	}
    os << endl;

	symbol::referencesT const& references = get_references();
    if (references.size() > 0 && find_if(references.begin(),references.end(), ReferenceNotNull_Pred()) != references.end())  {
		os << "     Referenced at: " << endl;
		for (symbol::referencesT::const_iterator itr = references.begin(); 
			 itr != references.end(); itr++) {
	        os << "     " << (itr->second?itr->second->to_str():"") << endl;
        }
    }

	return os;
}

std::ostream& operator<<(std::ostream& os, symbol const& smb) {
	return	smb.summary(os); 
}

symbol const smb_FIRST_TOKEN(T_FIRST_TOKEN, 0);
}  // namespace cnome
